package com.erp.util.proxy;

import com.erp.util.db.DataBaseDao;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

/**
 * @author lpy
 * 业务逻辑的代理类,该类负责数据库事务的控制以及数据库链接的获取和资源关闭
 */
public class ServiceProxy extends DataBaseDao implements InvocationHandler {
    /**
     * 真实业务对象
     */
    private Object target;

    //需要手动开启事务的方法名称头部集合
    private static final List<String> TRANSCATION_METHOD_HEAD = Arrays.asList("add","update","remove","edit","delete","create");

    /**
     * 绑定真实业务对象,并返回代理类对象
     * @param target 真实业务接口的对象,利用反射可追溯其来源
     * @return 代理类对象
     */
    public Object band(Object target){
        this.target = target;
        //代理类动态实现了真实业务的父接口(target的父接口),这是由Java底层自己去实现的
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
    }

    /**
     * 判断方法是否需要手动开启事务
     * @param methodName 方法名称
     * @return
     */
    public boolean openTranscation(String methodName){
        Iterator<String> iterator = TRANSCATION_METHOD_HEAD.iterator();
        while (iterator.hasNext()){
            if (methodName.startsWith(iterator.next())){
                return true;
            }
        }
        return false;
    }

    /**
     *@param proxy  代理对象
     *@param method 业务接口执行的方法
     *@param args   方法的参数
     *@return 执行结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
        Object result = null;
        try{
            //加载数据库连接
            buildConnection();
            //判断要执行的方法是否需要手动开启事务
            if (this.openTranscation(method.getName())) {
                //关闭自动提交事务
                conn.setAutoCommit(false);
            }

            //执行真实业务方法
            result = method.invoke(this.target,args);

            if (this.openTranscation(method.getName())) {
                //提交事务
                conn.commit();
            }
        }catch (Exception e){
            if (this.openTranscation(method.getName())) {
                //产生异常,回滚事务
                conn.rollback();
            }
            throw e;
        }finally {
            //关闭资源
            conn.close();
        }
        return result;
    }
}
